package wdevent

import (
	"sync"
	"time"
)

const wdContextEventDefaultMessage = "contextEventDefaultMessage"

type Context struct {
	status   bool
	timeout  time.Duration
	maxCycle int
	global   chan EventHandle
	events   chan EventHandle
	doneChan chan struct{}
	coll     sync.Map
}

func NewContext() *Context {
	return &Context{
		global:   nil,
		events:   nil,
		doneChan: make(chan struct{}),
		coll:     sync.Map{},
	}
}
func (c *Context) SetMaxCycle(max int) *Context {
	c.maxCycle = max
	return c
}
func (c *Context) SetTimeout(to time.Duration) *Context {
	c.timeout = to
	return c
}
func (c *Context) setContextByEventHandle(global []EventHandle, middle []EventHandle, events []EventHandle) *Context {
	//fmt.Println(len(global), len(middle), len(events))
	glen := len(global) + len(middle) + 1
	elen := len(events) + 1
	c.global = make(chan EventHandle, glen)
	c.events = make(chan EventHandle, elen)
	ctx := c
	for i, _ := range global {
		ctx.global <- global[i]
	}
	for i, _ := range middle {
		ctx.global <- middle[i]
	}
	for i, _ := range events {
		ctx.events <- events[i]
	}
	close(ctx.global)
	close(ctx.events)
	return ctx
}
func (c *Context) Insert(key interface{}, value interface{}) *Context {
	c.coll.Store(key, value)
	return c
}
func (c *Context) InsertDefault(value interface{}) *Context {
	return c.Insert(wdContextEventDefaultMessage, value)
}
func (c *Context) Load(key interface{}) (interface{}, bool) {
	return c.coll.Load(key)
}
func (c *Context) LoadOrNil(key interface{}) interface{} {
	res, _ := c.coll.Load(key)
	return res
}
func (c *Context) LoadDefault() interface{} {
	return c.LoadOrNil(wdContextEventDefaultMessage)
}

func (c *Context) Next() error {
	next := c
	for true {
		if gHandle, ok := <-next.global; ok {
			if err := gHandle(next); err != nil {
				return err
			}
		}
		if eHandle, ok := <-next.events; ok {
			next.maxCycle--
			if next.status {
				return ErrorEventTermination
			}
			if next.maxCycle < 0 {
				return ErrorCycleTransfinite
			}
			if err := eHandle(next); err != nil {
				return err
			}
		} else {
			return nil
		}
	}
	return nil
}
func (c *Context) Abort() {
	c.status = true
}

func (c *Context) Deadline() (deadline time.Time, ok bool) {
	return time.Now(), false
}
func (c *Context) Done() <-chan struct{} {
	return c.doneChan
}
func (c *Context) Err() error {
	return nil
}
func (c *Context) Value(key interface{}) interface{} {
	if value, ok := c.coll.Load(key); ok {
		return value
	}
	return nil
}
